package dao

import (
	"errors"
	"gorm-demo/dao/bo"
	"gorm-demo/lib/db"
	"gorm-demo/model"
	"sync"
	"time"
)

type IStock interface {
	CreateStock(stock model.Stock) (id uint64, err error)
	// DeductionStock 批量扣减库存(本能用，只是用来举例子)
	DeductionStock(deductionStockInfoList []bo.DeductionStockInfo) (err error)
	// DeductionStockBegin 批量扣减库存支持事务
	DeductionStockBegin(deductionStockInfoList []bo.DeductionStockInfo) (err error)
	// DeductionStockBeginMutex 批量扣减库存支持事务和锁
	DeductionStockBeginMutex(deductionStockInfoList []bo.DeductionStockInfo) (err error)
	// AddStockBeginMutex 批量添加库存支持事务和锁
	AddStockBeginMutex(deductionStockInfoList []bo.AddStockInfo) (err error)
}

type Stock struct {
}

func (_ Stock) CreateStock(stock model.Stock) (id uint64, err error) {
	stock.Id = uint64(time.Now().UnixNano())
	result := db.GetDB().Create(stock)
	if result.Error != nil {
		return 0, result.Error
	}
	return stock.Id, nil
}

// DeductionStock 模拟购物车提交，批量扣减商品库存
func (_ Stock) DeductionStock(deductionStockInfoList []bo.DeductionStockInfo) (err error) {
	_db := db.GetDB()
	for _, v := range deductionStockInfoList {
		// 1.查询商品库存
		stock := model.Stock{}
		result := _db.Where(&model.Stock{GoodsId: v.GoodsId}).First(&stock)
		if result.Error != nil {
			return result.Error
		}
		if result.RowsAffected == 0 {
			return errors.New("没有库存信息")
		}
		if stock.Qty == 0 {
			return errors.New("暂无库存")
		}

		if stock.Qty < v.Num {
			return errors.New("库存不足")
		}

		// 2.执行扣减
		stock.Qty = stock.Qty - v.Num
		result = _db.Save(stock)
		if result.Error != nil {
			return result.Error
		}
	}
	return nil
}

func (_ Stock) DeductionStockBegin(deductionStockInfoList []bo.DeductionStockInfo) (err error) {
	_db := db.GetDB()
	// 开启事务
	tx := _db.Begin()
	for _, v := range deductionStockInfoList {
		// 1.查询商品库存
		stock := model.Stock{}
		result := _db.Where(&model.Stock{GoodsId: v.GoodsId}).First(&stock)
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			return result.Error
		}
		if result.RowsAffected == 0 {
			// 回滚事务
			tx.Rollback()
			return errors.New("没有库存信息")
		}
		if stock.Qty == 0 {
			// 回滚事务
			tx.Rollback()
			return errors.New("暂无库存")
		}

		if stock.Qty < v.Num {
			// 回滚事务
			tx.Rollback()
			return errors.New("库存不足")
		}

		// 2.执行扣减
		stock.Qty = stock.Qty - v.Num
		result = tx.Save(stock) // 这里也要改成事务更新
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			return result.Error
		}
	}
	// 提交事务
	tx.Commit()
	return nil
}

var deductionStockBeginMutex sync.Mutex // 互斥锁
// DeductionStockBeginMutex 模拟购物车提交，批量扣减商品库存，支持事务版本
func (_ Stock) DeductionStockBeginMutex(deductionStockInfoList []bo.DeductionStockInfo) (err error) {
	_db := db.GetDB()
	// 加锁
	deductionStockBeginMutex.Lock()
	// 开启事务
	tx := _db.Begin()
	for _, v := range deductionStockInfoList {
		// 1.查询商品库存
		stock := model.Stock{}
		result := _db.Where(&model.Stock{GoodsId: v.GoodsId}).First(&stock)
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			deductionStockBeginMutex.Unlock()
			return result.Error
		}
		if result.RowsAffected == 0 {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			deductionStockBeginMutex.Unlock()
			return errors.New("没有库存信息")
		}
		if stock.Qty == 0 {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			deductionStockBeginMutex.Unlock()
			return errors.New("暂无库存")
		}

		if stock.Qty < v.Num {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			deductionStockBeginMutex.Unlock()
			return errors.New("库存不足")
		}

		// 2.执行扣减
		stock.Qty = stock.Qty - v.Num
		result = tx.Save(stock) // 这里也要改成事务更新
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			deductionStockBeginMutex.Unlock()
			return result.Error
		}
	}
	// 提交事务
	tx.Commit()
	// 释放锁
	deductionStockBeginMutex.Unlock()
	return nil
}

var addStockBeginMutex sync.Mutex // 互斥锁
func (_ Stock) AddStockBeginMutex(deductionStockInfoList []bo.AddStockInfo) (err error) {
	_db := db.GetDB()
	// 加锁
	addStockBeginMutex.Lock()
	// 开启事务
	tx := _db.Begin()
	for _, v := range deductionStockInfoList {
		// 1.查询商品库存
		stock := model.Stock{}
		result := _db.Where(&model.Stock{GoodsId: v.GoodsId}).First(&stock)
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			addStockBeginMutex.Unlock()
			return result.Error
		}
		if result.RowsAffected == 0 {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			addStockBeginMutex.Unlock()
			return errors.New("没有库存信息")
		}

		// 2.执行添加
		stock.Qty = stock.Qty + v.Num
		result = tx.Save(stock) // 这里也要改成事务更新
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			// 释放锁
			addStockBeginMutex.Unlock()
			return result.Error
		}
	}
	// 提交事务
	tx.Commit()
	// 释放锁
	addStockBeginMutex.Unlock()
	return nil
}

func NewStock() IStock {
	return Stock{}
}
